import { Next, Request, Response } from "restify";
import errors from "restify-errors";

export const requireParameter = (
  requiredParameter: string,
  conditions: ((parameter: any) => boolean)[] | undefined = undefined,
  required = true
) => {
  return (target: any, methodName: string, descriptor: PropertyDescriptor) => {
    const method = descriptor.value;
    descriptor.value = function (
      req: Request,
      res: Response,
      next: Next
    ): void {
      const exist =
        req.body !== undefined && req.body[requiredParameter] !== undefined;

      if (required && !exist) {
        next(new errors.MissingParameterError(requiredParameter));

        return;
      }
      if (exist && conditions !== undefined) {
        for (const condition of conditions) {
          if (!condition(req.body[requiredParameter])) {
            next(new errors.InvalidArgumentError(requiredParameter));

            return;
          }
        }
      }

      /* tslint:disable no-invalid-this */
      return method.apply(this, [req, res, next]);
      /* tslint:enable no-invalid-this */
    };
  };
};

export const requireParameterCompare = <T>(
  parameter1: string,
  parameter2: string,
  comparator: (param1: T, param2: T) => boolean
) => {
  return (target: any, methodName: string, descriptor: PropertyDescriptor) => {
    const method = descriptor.value;
    descriptor.value = function (
      req: Request,
      res: Response,
      next: Next
    ): void {
      if (!comparator(req.body[parameter1] as T, req.body[parameter2] as T)) {
        next(new errors.InvalidArgumentError(`${parameter1}, ${parameter2}`));

        return;
      }

      /* tslint:disable no-invalid-this */
      return method.apply(this, [req, res, next]);
      /* tslint:enable no-invalid-this */
    };
  };
};
